#include <SI4735.h>

#include "si5351_lite.h"

#include "radio.h"

#define SSB_PATCH

#ifdef SSB_PATCH
#include <patch_init.h>
#else
#include <patch_ssb_compressed.h>

#endif

Si5351 si5351;
SI4735 si473x;

#define FM_CURRENT_MODE 0
#define AM_CURRENT_MODE 1

#define FM 0
#define AM 1
#define LW 2

#define SW3 3
#define SSB 4

#define FM_STEP 10
#define AM_STEP 1
#define SW_STEP 1
#define SSB_STEP 1
#define AIR_STEP 100

#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
#define RESET_PIN 2  // 16
#elif defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_IDF_TARGET_ESP32S3)
#define RESET_PIN 42  // 16
#else                 //STM32F103C8T6

#endif


#define LSB 0
#define USB 1

#define AGC_MAX 35
#define AGC_MIN 0

#define AVC_MIN 12
#define AVC_MAX 90

const char *radio_init();
void radio_fm(int);
void radio_am(int);
void radio_ssb(int);
uint16_t getAirFreq();
uint8_t radio_getSnr();
void setAirFreq(uint16_t freq);

typedef void (*bandFunc)(int);

typedef struct {
  const char *freqName;
  Radio_Band_Type bandType;
  uint16_t minimumFreq;
  uint16_t maximumFreq;
  uint16_t currentFreq;
  uint16_t currentStep;
  uint16_t currentVolume;
  uint8_t usblsb;
  int bfo;
  int8_t agc;
  int8_t avc;
  uint8_t bwIndex;
  bandFunc funcPtr;
} Band;

Band band[] = {
  { "FM", FM_BAND_TYPE, 6400, 10800, 9480, FM_STEP, 55, 0, 0, 0, 0, 0, radio_fm },
  { "AM", AM_BAND_TYPE, 520, 3500, 540, AM_STEP, 55, 0, 0, 0, 32, 4, radio_am },
  { "LW", AM_BAND_TYPE, 153, 279, 164, AM_STEP, 55, 0, 0, 0, 48, 0, radio_am },
  { "80M", SSB_BAND_TYPE, 3500, 4000, 3500, SSB_STEP, 55, LSB, 0, 0, 32, 0, radio_ssb },
  { "SW1", SW_BAND_TYPE, 4500, 5200, 5000, AM_STEP, 55, 0, 0, 0, 32, 4, radio_am },
  { "SW2", SW_BAND_TYPE, 5700, 6300, 6000, AM_STEP, 55, 0, 0, 0, 32, 0, radio_am },
  { "40M", SSB_BAND_TYPE, 7000, 7200, 7050, SSB_STEP, 55, LSB, -465, 0, 40, 0, radio_ssb },
  { "SW3", SW_BAND_TYPE, 7200, 7900, 7430, AM_STEP, 55, 0, 0, 0, 40, 0, radio_am },
  { "SW4", SW_BAND_TYPE, 9200, 10000, 10000, AM_STEP, 55, 0, 0, 0, 40, 4, radio_am },
  { "SW5", SW_BAND_TYPE, 10700, 13000, 10700, AM_STEP, 55, 0, 0, 0, 40, 0, radio_am },
  // 13.149是海事频道
  { "SW6", SW_BAND_TYPE, 13100, 13900, 13500, AM_STEP, 55, 0, 0, 0, 40, 0, radio_am },
  { "20M", SSB_BAND_TYPE, 14000, 15000, 14275, SSB_STEP, 55, USB, 125, 0, 42, 2, radio_ssb },
  { "SW7", SW_BAND_TYPE, 15000, 16000, 15000, AM_STEP, 55, 0, 0, 0, 42, 4, radio_am },
  { "SW8", SW_BAND_TYPE, 17300, 17900, 17500, AM_STEP, 55, 0, 0, 0, 42, 0, radio_am },
  { "15M", SSB_BAND_TYPE, 20000, 21400, 21100, SSB_STEP, 55, USB, 0, 0, 44, 0, radio_ssb },
  { "SW9", SW_BAND_TYPE, 21450, 21900, 21500, AM_STEP, 55, 0, 0, 0, 44, 0, radio_am },
  { "CB ", SW_BAND_TYPE, 26000, 28000, 27500, AM_STEP, 55, 0, 0, 0, 44, 0, radio_am },
  { "10M", SSB_BAND_TYPE, 28000, 30000, 28400, SSB_STEP, 55, USB, 0, 0, 44, 0, radio_ssb },
  { "ALL", AM_BAND_TYPE, 150, 30000, 150, AM_STEP, 55, 0, 0, 0, 48, 0, radio_am },
  { "AIR", AIR_BAND_TYPE, 10700, 10700, 10700, AM_STEP, 55, 0, 0, 0, 48, 4, radio_am }
};

const int lastBand = (sizeof band / sizeof(Band)) - 1;
short bandIndex = lastBand;
int8_t ssb_bandwidthIdx = 0;
int8_t bandwidthIdx = 0;
int tabStep[7] = { 1, 5, 10, 50, 100, 500, 1000 };
int8_t tabStepIndex = 0;
const int lastStep = (sizeof tabStep / sizeof(int)) - 1;

const char *ssb_bandwidth[6] = { "1.2", "2.2", "3.0", "4.0", "0.5", "1.0" };
const char *bandwidth[7] = { "6.0", "4.0", "3.0", "2.0", "1.0", "1.8", "2.5" };

const char *radio_band_selection(short index) {
  if (index >= 1) {
    if (bandIndex == lastBand) {
      bandIndex = 0;
    } else {
      bandIndex += 1;
    }
    band[bandIndex].funcPtr(bandIndex);
  } else if (index <= -1) {
    if (bandIndex == 0) {
      bandIndex = lastBand;
    } else {
      bandIndex -= 1;
    }
    band[bandIndex].funcPtr(bandIndex);
  }

  return band[bandIndex].freqName;
}

const char *radio_freq_name() {
  return band[bandIndex].freqName;
}

const char *radio_bandWidth() {
  uint8_t index = band[bandIndex].bwIndex;
  if (band[bandIndex].bandType == AM_BAND_TYPE || band[bandIndex].bandType == SW_BAND_TYPE || band[bandIndex].bandType == AIR_BAND_TYPE) {
    return bandwidth[index];
  } else if (band[bandIndex].bandType == SSB_BAND_TYPE) {
    return ssb_bandwidth[index];
  }
  return "auto";
}

int8_t radio_agc() {
  return band[bandIndex].agc;
}

void radio_setAgc(int8_t idx) {
  if (idx < AGC_MIN) {
    idx = AGC_MAX;
  } else if (idx > AGC_MAX) {
    idx = AGC_MIN;
  }
  si473x.setAutomaticGainControl(idx == 0 ? 0 : 1, idx);
  si473x.getAutomaticGainControl();
  Serial.print((si473x.isAgcEnabled()) ? "AGC ON " : "AGC OFF");
  Serial.print(" | LNA GAIN index: ");
  band[bandIndex].agc = si473x.getAgcGainIndex();
  Serial.println(band[bandIndex].agc);
  delay(1000);
}

int8_t radio_avc() {
  return band[bandIndex].avc;
}

void radio_setAvc(uint8_t gain) {
  if (gain > AVC_MAX) {
    gain = AVC_MIN;
  } else if (gain < AVC_MIN) {
    gain = AVC_MAX;
  }
  si473x.setAvcAmMaxGain(gain);
  band[bandIndex].avc = si473x.getCurrentAvcAmMaxGain();
  delay(1000);
}

int radio_bfo() {
  return band[bandIndex].bfo;
}

void radio_setBfo(int bfo) {
  Serial.println("set bfo:" + String(bfo));
  si473x.setSSBBfo(bfo);
  band[bandIndex].bfo = bfo;
}

const char *radio_pilot() {
  if (band[bandIndex].bandType == FM_BAND_TYPE) {
    return si473x.getCurrentPilot() ? "ST" : "MO";
  } else if (band[bandIndex].bandType == AM_BAND_TYPE || band[bandIndex].bandType == SW_BAND_TYPE || band[bandIndex].bandType == AIR_BAND_TYPE) {
    if (strcmp(band[bandIndex].freqName, "ALL") == 0) {
      return "AM";
    }
    return "";
  } else if (band[bandIndex].bandType == SSB_BAND_TYPE) {
    return band[bandIndex].usblsb == LSB ? "L" : "U";
  }
  return "";
}
bool radio_is_ssb() {
  if (band[bandIndex].bandType == SSB_BAND_TYPE) {
    return true;
  }
  return false;
}
bool radio_lbub_ssb(int index) {
  if (band[bandIndex].bandType == SSB_BAND_TYPE) {
    if (index > 0) {
      si473x.setSSB(USB);
      band[bandIndex].usblsb = USB;
    } else {
      si473x.setSSB(LSB);
      band[bandIndex].usblsb = LSB;
    }
    return true;
  }
  return false;
}

const char *radio_getFrequencyStep() {
  int rs = tabStep[tabStepIndex];
  if (rs >= 1000) {
    return (String(rs / 1000) + "k").c_str();
  }
  return String(rs).c_str();
}

void radio_setFrequencyStep(int index) {
  if (index > 0) {
    tabStepIndex++;
    if (tabStepIndex > lastStep) {
      tabStepIndex = 0;
    }
  } else if (index < 0) {
    tabStepIndex--;
    if (tabStepIndex < 0) {
      tabStepIndex = lastStep;
    }
  }
}


const char *radio_init() {
  Serial.println("Si473x init");

  digitalWrite(RESET_PIN, HIGH);

  // Wire.beginTransmission(0x11);
  int16_t si4735Addr = si473x.getDeviceI2CAddress(RESET_PIN);
  if (si4735Addr == 0) {
    Serial.println("Si473X not found!");
    Serial.flush();
    return "Si473X not found!";
  } else {
    Serial.print("The Si473X I2C address is 0x");
    Serial.println(si4735Addr, HEX);
  }
  return ("The Si473X \nI2C address \nis 0x" + String(si4735Addr, HEX)).c_str();
}

uint16_t radio_getCurrentFreq() {
  if (band[bandIndex].bandType == AIR_BAND_TYPE) {
    Serial.println("radio_getCurrentFreq bandType == > AIR_BAND_TYPE");
    return getAirFreq();
  }
  return si473x.getCurrentFrequency();
}

uint16_t radio_freq(int16_t freq) {

  if (band[bandIndex].bandType == AIR_BAND_TYPE) {
    Serial.println("radio_freq bandType == > AIR_BAND_TYPE");
    uint16_t nextFreq = getAirFreq() + (freq * tabStep[tabStepIndex]);
    setAirFreq(nextFreq);
    return getAirFreq();
  } else {
    uint16_t nextFreq = si473x.getCurrentFrequency() + (freq * tabStep[tabStepIndex]);
    if (nextFreq < band[bandIndex].minimumFreq) {
      si473x.setFrequency(band[bandIndex].maximumFreq);
    } else if (nextFreq > band[bandIndex].maximumFreq) {
      si473x.setFrequency(band[bandIndex].minimumFreq);
    } else {
      si473x.setFrequency(nextFreq);
    }
    return si473x.getCurrentFrequency();
  }
}

void radio_stationUp(void (*showFunc)(uint16_t f)) {
  if(band[bandIndex].bandType == AIR_BAND_TYPE){
    while (true) {
      uint16_t nAirFreq = getAirFreq() + 1;
      setAirFreq(nAirFreq);
     // showFunc(nAirFreq);
      uint8_t snr = radio_getSnr();
      Serial.print("SNR:");
      Serial.println( snr);
      if (snr > 3 && snr < 100) {
         showFunc(nAirFreq);
        break;
      }
    }
  }else{
    if (si473x.getCurrentFrequency() == band[bandIndex].maximumFreq) {
      si473x.setFrequency(band[bandIndex].minimumFreq);
    }
    si473x.seekStationProgress(showFunc, 1);
  }
}

void radio_stationDown(void (*showFunc)(uint16_t f)) {
  if (si473x.getCurrentFrequency() == band[bandIndex].minimumFreq) {
    si473x.setFrequency(band[bandIndex].maximumFreq);
  }
  si473x.seekStationProgress(showFunc, 0);
}

uint8_t radio_getVolume() {
  return si473x.getVolume();
}

uint8_t radio_setVolume(uint8_t volume) {
  if (volume > 63) {
    si473x.setVolume(63);
  } else {
    si473x.setVolume(volume);
  }
  return si473x.getVolume();
}

void setBandWidth(int8_t bwIndex, Radio_Band_Type type) {
  if (type == SSB_BAND_TYPE) {
    si473x.setSSBAudioBandwidth(bwIndex);
    Serial.print("SSB Filter - Bandwidth: ");
    Serial.print(ssb_bandwidth[bwIndex]);
    Serial.println(" kHz index:" + String(bwIndex));
    // If audio bandwidth selected is about 2 kHz or below, it is recommended to
    // set Sideband Cutoff Filter to 0.
    if (bwIndex == 0 || bwIndex == 4 || bwIndex == 5)
      si473x.setSSBSidebandCutoffFilter(0);
    else
      si473x.setSSBSidebandCutoffFilter(1);
  } else {
    si473x.setBandwidth(bwIndex, 1);
    Serial.print("Filter - Bandwidth: ");
    Serial.print(bandwidth[bwIndex]);
    Serial.println(" kHz index:" + String(bwIndex));
  }
}

void radio_setBandWidth(int8_t bw) {
  if (band[bandIndex].bandType == SSB_BAND_TYPE) {
    if (bw == 0) {
      ssb_bandwidthIdx = 0;
    } else {
      ssb_bandwidthIdx += bw;
    }
    if (ssb_bandwidthIdx > 5) {
      ssb_bandwidthIdx = 0;
    } else if (ssb_bandwidthIdx < 0) {
      ssb_bandwidthIdx = 5;
    }
    band[bandIndex].bwIndex = ssb_bandwidthIdx;
    setBandWidth(ssb_bandwidthIdx, SSB_BAND_TYPE);
  } else {
    if (!si473x.isCurrentTuneFM()) {
      if (bw == 0) {
        bandwidthIdx = 0;
      } else {
        bandwidthIdx += bw;
      }
      if (bandwidthIdx > 6) {
        bandwidthIdx = 0;
      } else if (bandwidthIdx < 0) {
        bandwidthIdx = 6;
      }
      band[bandIndex].bwIndex = bandwidthIdx;
      setBandWidth(bandwidthIdx, AM_BAND_TYPE);
    }
  }
}


void radio_fm(int index) {

  si473x.setup(RESET_PIN, FM_CURRENT_MODE);
  si473x.setFM(band[index].minimumFreq, band[index].maximumFreq, band[index].currentFreq,
               band[index].currentStep);
  delay(500);
  si473x.setSeekFmLimits(band[index].minimumFreq, band[index].maximumFreq);
  si473x.setFmBandwidth(0);
  si473x.setRdsConfig(1, 2, 2, 2, 2);
  si473x.setFifoCount(1);
  si473x.setSeekAmRssiThreshold(0);
  si473x.setSeekAmSNRThreshold(10);
  si473x.setFrequency(band[index].currentFreq);
  si473x.setVolume(band[index].currentVolume);
}

void radio_am(int index) {
  Serial.println("Si473X AM");
  si473x.setup(RESET_PIN, AM_CURRENT_MODE);
  si473x.setAM(band[index].minimumFreq, band[index].maximumFreq, band[index].currentFreq,
               band[index].currentStep);
  si473x.setSeekAmLimits(band[index].minimumFreq, band[index].maximumFreq);
  si473x.setAmSoftMuteMaxAttenuation(4);
  si473x.setAvcAmMaxGain(band[index].avc);
  setBandWidth(band[index].bwIndex, AM_BAND_TYPE);
  si473x.setVolume(band[index].currentVolume);
}

void patch_ssb() {
  Serial.println("load ssb patch");
  uint16_t size_content = sizeof ssb_patch_content;
  // loadSSB
  si473x.setI2CFastModeCustom(500000);                // Increase the transfer I2C speed
  si473x.loadPatch(ssb_patch_content, size_content);  // It is a legacy  function. See loadCompressedPatch
  si473x.setI2CFastModeCustom(100000);                // Set standard transfer I2C speed
}

void patch_ssb_compressed() {
  Serial.println("load ssb compressed patch");
/**压缩补丁**/
#ifndef SSB_PATCH
  uint16_t size_content = sizeof ssb_patch_content;
  uint16_t cmd_0x15_size = sizeof cmd_0x15;
  si473x.loadCompressedPatch(ssb_patch_content, size_content, cmd_0x15,
                             cmd_0x15_size);
#endif
}

bool is_all() {
  return !strcmp(band[bandIndex].freqName, "ALL");
}


void radio_ssb(int index) {
  si473x.setup(RESET_PIN, AM_CURRENT_MODE);
#ifdef SSB_PATCH
  patch_ssb();
#else
  patch_ssb_compressed();
#endif
  si473x.setTuneFrequencyAntennaCapacitor(1);
  si473x.setSSB(band[index].minimumFreq, band[index].maximumFreq, band[index].currentFreq,
                band[index].currentStep, band[index].usblsb);
  // BandWitdh
  // si473x.setSSBAudioBandwidth(2);
  // si473x.setSSBSidebandCutoffFilter(1);
  setBandWidth(band[index].bwIndex, SSB_BAND_TYPE);
  si473x.setSSBAutomaticVolumeControl(1);
  si473x.setSsbSoftMuteMaxAttenuation(4);
  si473x.setAvcAmMaxGain(band[index].avc);
  si473x.setSSBBfo(band[index].bfo);
  si473x.setVolume(band[index].currentVolume);
}

int sw = 0;
void all_am_ssb_switch() {
  switch (sw) {
    case 0:
      radio_am(bandIndex);
      sw = 1;
      break;
    case 1:
      radio_ssb(bandIndex);
      band[bandIndex].usblsb = LSB;
      si473x.setSSB(LSB);
      sw = 2;
      break;
    case 2:
      band[bandIndex].usblsb = USB;
      si473x.setSSB(USB);
      sw = 0;
      break;
  }
}

void radio_getCurrentReceivedSignalQuality() {
  si473x.getCurrentReceivedSignalQuality();
}
//信噪比
uint8_t radio_getSnr() {
  return si473x.getCurrentSNR();
}

//信号强度
uint8_t radio_getRssi() {
  return si473x.getCurrentRSSI();
}

//                              3276800 32.768khz
#define mhz 100  // Hz
#define SI4735_AIR_FREQ_MIN     117000 /*khz 118 Mhz  si4732 */
#define SI4735_AIR_FREQ_MAX     134000 /*khz 118 Mhz  si4732 */
#define SI5351_AIR_FREQ_MAX SI4735_AIR_FREQ_MAX * mhz /* 134 Mhz */
#define SI5351_AIR_FREQ_MIN SI4735_FREQ_MIN * mhz /* si5351  */

#define SI4735_FREQ_10_7Mhz  10700 /* 10.7 Mhz*/

#define DEFINE_AIR_FREQ 118100

uint32_t airFreq = SI4735_AIR_FREQ_MIN;

uint16_t getAirFreq() {
  Serial.print("get airline freq:");
  Serial.println(airFreq);
  return airFreq / 10;
}

void setAirFreq(uint16_t freq) {
  uint64_t nextFreq = freq * 10 ;
  airFreq = nextFreq;
  Serial.println(SI4735_AIR_FREQ_MIN);
  Serial.println(nextFreq);
  Serial.println(SI4735_AIR_FREQ_MAX);
  if (nextFreq < SI4735_AIR_FREQ_MIN) {
    airFreq = SI4735_AIR_FREQ_MAX;
    Serial.println("nexFreq < SI5351_AIR_FREQ_MIN:" + String(SI4735_AIR_FREQ_MAX));
    si5351.set_freq((SI4735_AIR_FREQ_MAX - SI4735_FREQ_10_7Mhz) * mhz, SI5351_CLK1);
  } else if (nextFreq > SI4735_AIR_FREQ_MAX) {
    airFreq = SI4735_AIR_FREQ_MIN;
    Serial.println("nexFreq > SI5351_AIR_FREQ_MAX:" + String(SI4735_AIR_FREQ_MIN));
    si5351.set_freq((SI4735_AIR_FREQ_MIN - SI4735_FREQ_10_7Mhz) * mhz, SI5351_CLK1);
  } else {
    uint64_t mairfreq = (nextFreq - SI4735_FREQ_10_7Mhz) * mhz;
    Serial.println(mairfreq);
    si5351.set_freq(mairfreq, SI5351_CLK1);
  }
  si5351.output_enable(SI5351_CLK1, 1);
  si5351.update_status();
}

void si5351e() {
  if (!si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0)) {
    Serial.println("Device not found on I2C bus!");
    delay(500);
  }
  // si5351.set_correction(CORRECTION_FACTOR, SI5351_PLL_INPUT_XO);
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  //si5351.set_freq(1000000000, SI5351_CLK0); // For calibration
  //32.768kHz
  si5351.set_freq(3276800, SI5351_CLK0);  // Start CLK0 (VFO)
  //  si5351.set_freq(3276800, SI5351_CLK0);  // Start CLK0 (VFO)
  si5351.output_enable(SI5351_CLK0, 1);
  si5351.output_enable(SI5351_CLK2, 0);
  setAirFreq(DEFINE_AIR_FREQ);
  //si5351.update_status();
}